package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private AlbumAttributeValueMapper albumAttributeValueMapper;

    @Autowired
    private AlbumStatMapper albumStatMapper;

    @Autowired
    private VodService vodService;

    @Autowired
    private RabbitService rabbitService;


    /**
     * 内容创作者/运营人员保存专辑
     * * TODO 业务校验-验证内容安全
     * * 1.封装专辑相关信息，保存一条记录到专辑信息表
     * * 2.封装专辑标签关系集合，保存若干条记录到专辑标签关系表
     * * 3.封装专辑统计信息，保存4条记录到专辑统计表
     *
     * @param albuminfo 新增专辑信息
     * @param userId    用户ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class) //默认RuntimeException跟Error回滚事务
    public void saveAlbumInfo(AlbumInfo albuminfo, Long userId) {
        //1.封装专辑相关信息，保存一条记录到专辑信息表
        //1.1 设置用户ID
        albuminfo.setUserId(userId);
        //1.2 设置免费试听集数
        albuminfo.setTracksForFree(5);
        //1.3 设置专辑内容审核状态
        albuminfo.setStatus(SystemConstant.ALBUM_STATUS_NO_PASS);
        //1.4 保存专辑，获取专辑ID
        albumInfoMapper.insert(albuminfo);
        Long albumId = albuminfo.getId();
        //2.封装专辑标签关系集合，保存若干条记录到专辑标签关系表
        //2.1 获取入参中包含标签列表
        List<AlbumAttributeValue> albumAttributeValueVoList = albuminfo.getAlbumAttributeValueVoList();
        if (CollectionUtil.isNotEmpty(albumAttributeValueVoList)) {
            //2.2 遍历标签列表封装专辑标签关系对象
            for (AlbumAttributeValue albumAttributeValue : albumAttributeValueVoList) {
                albumAttributeValue.setAlbumId(albumId);
                //2.3 保存专辑标签关系
                albumAttributeValueMapper.insert(albumAttributeValue);
            }
        }
        //3.封装专辑统计信息，保存4条记录到专辑统计表
        this.saveAlbumInfoStat(albumId, SystemConstant.ALBUM_STAT_PLAY, 0);
        this.saveAlbumInfoStat(albumId, SystemConstant.ALBUM_STAT_SUBSCRIBE, 0);
        this.saveAlbumInfoStat(albumId, SystemConstant.ALBUM_STAT_BUY, 0);
        this.saveAlbumInfoStat(albumId, SystemConstant.ALBUM_STAT_COMMENT, 0);

        //4.TODO 业务校验 验证专辑内填写相关文本信息是否合法 根据审核结果设置审核状态
        String suggestTitle = vodService.scanText(albuminfo.getAlbumTitle());
        String suggestIntro = vodService.scanText(albuminfo.getAlbumIntro());
        if ("pass".equals(suggestTitle) && "pass".equals(suggestIntro)) {
            //专辑标题内容审核无误修改为审核通过
            albuminfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
            albumInfoMapper.updateById(albuminfo);
            //5.发送专辑上架MQ消息
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albuminfo.getId());
            return;
        }
        //6.发送专辑下架消息
        rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, albuminfo.getId());
        throw new GuiguException(500, "专辑标题或内容存在违规！");
    }

    /**
     * 保存专辑统计信息
     *
     * @param albumId  专辑ID
     * @param statType 统计类型
     * @param statNum  统计数值 0401-播放量 0402-订阅量 0403-购买量 0403-评论数'
     */
    @Override
    public void saveAlbumInfoStat(Long albumId, String statType, int statNum) {
        AlbumStat albumStat = new AlbumStat();
        albumStat.setAlbumId(albumId);
        albumStat.setStatType(statType);
        albumStat.setStatNum(statNum);
        albumStatMapper.insert(albumStat);
    }

    /**
     * 分页查询当前用户专辑列表
     *
     * @param pageInfo       分页对象
     * @param userId         用户ID
     * @param albumInfoQuery 查询条件
     * @return
     */
    @Override
    public Page<AlbumListVo> getUserAlbumPage(Page<AlbumListVo> pageInfo, Long userId, AlbumInfoQuery albumInfoQuery) {
        return albumInfoMapper.getUserAlbumPage(pageInfo, userId, albumInfoQuery);
    }

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    /**
     * 根据专辑ID删除专辑
     * 1.判断该专辑是否关联声音
     * 2.删除专辑记录
     * 3.删除统计记录
     * 4.删除专辑标签记录
     *
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeAlbumInfo(Long id) {
        //1.判断该专辑是否关联声音-根据专辑ID查询声音表数量进行判断
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackInfo::getAlbumId, id);
        Long count = trackInfoMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new GuiguException(500, "该专辑下存在声音！");
        }
        //2.删除专辑记录
        albumInfoMapper.deleteById(id);
        //3.删除统计记录
        LambdaQueryWrapper<AlbumStat> statLambdaQueryWrapper = new LambdaQueryWrapper<>();
        statLambdaQueryWrapper.eq(AlbumStat::getAlbumId, id);
        albumStatMapper.delete(statLambdaQueryWrapper);
        //4.删除专辑标签记录
        LambdaQueryWrapper<AlbumAttributeValue> attributeValueLambdaQueryWrapper = new LambdaQueryWrapper<>();
        attributeValueLambdaQueryWrapper.eq(AlbumAttributeValue::getAlbumId, id);
        albumAttributeValueMapper.delete(attributeValueLambdaQueryWrapper);

        //5.TODO 发送下架消息
        rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, id);
    }

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 根据专辑ID查询专辑信息（包括专辑标签列表）
     * 通过Redisson分布式锁解决缓存击穿问题
     *
     * @param id 专辑ID
     * @return 专辑信息
     */
    //@Override
    //public AlbumInfo getAlbumInfo(Long id) {
    //    try {
    //        //1.优先从缓存Redis中获取业务数据
    //        //1.1 构建专辑业务数据Key 形式：前缀+专辑ID
    //        String dataKey = RedisConstant.ALBUM_INFO_PREFIX + id;
    //        //1.2 查询缓存中专辑信息
    //        AlbumInfo albumInfo = (AlbumInfo) redisTemplate.opsForValue().get(dataKey);
    //        if (albumInfo != null) {
    //            //2.如果命中缓存，直接返回业务数据即可（不需要查库）
    //            return albumInfo;
    //        }
    //
    //        //3.如果未命中缓存，先获取分布式锁
    //        //3.1 构建当前业务数据锁的Key 形式：业务key+锁的后缀 注意：锁的粒度尽可能小
    //        String lockKey = dataKey + RedisConstant.CACHE_LOCK_SUFFIX;
    //        //3.2 构建当前业务数据锁的对象
    //        RLock lock = redissonClient.getLock(lockKey);
    //        //3.3 尝试获取分布式锁 tryLock默认锁过期时间30s底层还有看门狗机制进行锁自动续期
    //        boolean flag = lock.tryLock();
    //        if (flag) {
    //            try {
    //                //4.获取分布锁锁成功，执行查询数据库，并设置缓存
    //                AlbumInfo albumInfoFromDB = this.getAlbumInfoFromDB(id);
    //                int ttl = RandomUtil.randomInt(100, 600);
    //                redisTemplate.opsForValue().set(dataKey, albumInfoFromDB, RedisConstant.ALBUM_TIMEOUT + ttl, TimeUnit.SECONDS);
    //                return albumInfoFromDB;
    //            } catch (Exception e) {
    //                throw new RuntimeException(e);
    //            } finally {
    //                //5.释放分布式锁
    //                lock.unlock();
    //            }
    //        } else {
    //            //6.获取分布式锁失败，进行自旋（自旋可能获取锁成功线程会将业务数据已经放入缓存）
    //            return this.getAlbumInfo(id);
    //        }
    //    } catch (Exception e) {
    //        log.error("[专辑服务]获取专辑信息锁服务异常异常：{},执行兜底处理方案：{}", e, "直接查询数据库");
    //        return this.getAlbumInfoFromDB(id);
    //    }
    //}

    /**
     * 抽取单独查询专辑数据业务方法
     *
     * @param id
     * @return
     */
    @Override
    @GuiGuCache(prefix = RedisConstant.ALBUM_INFO_PREFIX)
    public AlbumInfo getAlbumInfoFromDB(Long id) {
        //1.根据专辑ID查询专辑信息
        AlbumInfo albumInfo = albumInfoMapper.selectById(id);

        //TODO 业务校验，校验专辑状态是否为下架状态，只有下架状态专辑才可以被修改

        //2.根据专辑ID查询专辑标签列表
        LambdaQueryWrapper<AlbumAttributeValue> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AlbumAttributeValue::getAlbumId, id);
        List<AlbumAttributeValue> albumAttributeValues = albumAttributeValueMapper.selectList(queryWrapper);
        if (CollectionUtil.isNotEmpty(albumAttributeValues)) {
            albumInfo.setAlbumAttributeValueVoList(albumAttributeValues);
        }
        return albumInfo;
    }

    /**
     * 修改专辑信息
     *
     * @param albumInfo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAlbumInfo(AlbumInfo albumInfo) {
        //1.修改专辑相关信息
        albumInfoMapper.updateById(albumInfo);
        Long albumId = albumInfo.getId();
        //2.可能需要-修改专辑标签关系
        //3.1 根据专辑ID删除专辑标签关系记录
        LambdaQueryWrapper<AlbumAttributeValue> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AlbumAttributeValue::getAlbumId, albumId);
        albumAttributeValueMapper.delete(queryWrapper);

        //3.2 重新保存专辑标签关系
        List<AlbumAttributeValue> albumAttributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
        if (CollectionUtil.isNotEmpty(albumAttributeValueVoList)) {
            for (AlbumAttributeValue albumAttributeValue : albumAttributeValueVoList) {
                albumAttributeValue.setAlbumId(albumId);
                albumAttributeValueMapper.insert(albumAttributeValue);
            }
        }
        //TODO 对修改后内容进行再次内容审核
        String suggestTitle = vodService.scanText(albumInfo.getAlbumTitle());
        String suggestIntro = vodService.scanText(albumInfo.getAlbumIntro());
        if ("pass".equals(suggestTitle) && "pass".equals(suggestIntro)) {
            //专辑标题内容审核无误修改为审核通过
            albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
            albumInfoMapper.updateById(albumInfo);
            //4. TODO 发送上架消息
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumInfo.getId());
            return;
        }
        //5.发送下架消息
        rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, albumInfo.getId());
        throw new GuiguException(500, "专辑标题或内容存在违规！");
    }

    /**
     * 获取当前用户全部专辑列表
     *
     * @param userId
     * @return
     */
    @Override
    public List<AlbumInfo> getUserAllAlbumList(Long userId) {
        //1.构建查询条件QueryWrapper对象
        LambdaQueryWrapper<AlbumInfo> queryWrapper = new LambdaQueryWrapper<>();
        //1.1 查询条件
        queryWrapper.eq(AlbumInfo::getUserId, userId);
        //1.2 排序
        queryWrapper.orderByDesc(AlbumInfo::getId);
        //1.3 限制记录数
        queryWrapper.last("LIMIT 200");
        //1.4 指定查询列
        queryWrapper.select(AlbumInfo::getId, AlbumInfo::getAlbumTitle);
        //2.执行列表查询
        return albumInfoMapper.selectList(queryWrapper);
    }

    @Autowired
    private UserFeignClient userFeignClient;

    /**
     * 根据专辑ID查询专辑统计信息
     *
     * @param albumId
     * @return
     */
    @Override
    public AlbumStatVo getAlbumStatVo(Long albumId) {
        return albumInfoMapper.getAlbumStatVo(albumId);
    }

    /**
     * 基于当前选择购买起始声音得到分集购买列表
     *
     * @param userId  用户ID
     * @param trackId 选择购买声音ID（起始计算参照）
     * @return [{name:"本集",price:1,trackCount:1}]
     */
    @Override
    public List<Map<String, Object>> getUserTrackPaidList(Long userId, Long trackId) {
        //1.创建分集购买列表
        List<Map<String, Object>> list = new ArrayList<>();

        //2.根据声音ID查询到“起始”待购声音记录，得到当前声音徐浩、所属专辑ID
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        Integer orderNum = trackInfo.getOrderNum();
        Long albumId = trackInfo.getAlbumId();

        //3.根据"起始"声音序号获取所有待购买声音列表（可能包含用户已购声音）
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackInfo::getAlbumId, albumId);
        queryWrapper.ge(TrackInfo::getOrderNum, orderNum);
        queryWrapper.select(TrackInfo::getId);
        List<TrackInfo> trackInfoList = trackInfoMapper.selectList(queryWrapper);

        //4.远程调用"用户服务"获取已购买声音ID列表
        List<Long> userPaidTrackIdList = userFeignClient.getUserPaidTrackIdList(albumId).getData();

        //5.如果存在已购声音ID，将已购声音ID排除掉得到真正未购买声音列表
        if (CollectionUtil.isNotEmpty(userPaidTrackIdList)) {
            trackInfoList = trackInfoList.stream()
                    .filter(track -> !userPaidTrackIdList.contains(track.getId())).collect(Collectors.toList());
        }

        //6.根据未购买声音列表长度动态构建分集购买列表
        //6.0 根据专辑ID查询专辑价格=声音价格
        BigDecimal price = albumInfoMapper.selectById(albumId).getPrice();
        //6.1 构建"本集"分集购买对象
        Map<String, Object> currMap = new HashMap<>();
        currMap.put("name", "本集");
        currMap.put("price", price);
        currMap.put("trackCount", 1);
        list.add(currMap);
        //6.2 根据未购买声音数量循环最多构建6个分集购买对象（50集）
        int size = trackInfoList.size();
        //例如：size=12 展示 后10集，全集     size=35 展示 后10集 后20集 后30集 全集
        for (int i = 10; i <= 50; i += 10) {
            if (size > i) {
                Map<String, Object> map = new HashMap<>();
                map.put("name", "后" + i + "集");
                map.put("price", price.multiply(BigDecimal.valueOf(i)));
                map.put("trackCount", i);
                list.add(map);
            } else {
                Map<String, Object> map = new HashMap<>();
                map.put("name", "全集(" + size + "集）");
                map.put("price", price.multiply(BigDecimal.valueOf(size)));
                map.put("trackCount", size);
                list.add(map);
                break;
            }
        }
        return list;
    }

    /**
     * 查询当前用户未购买声音列表
     *
     * @param userId     用户ID
     * @param trackId    点击付费标识声音ID，将该声音作为起始标准
     * @param trackCount 购买数量
     * @return 待购声音列表
     */
    @Override
    public List<TrackInfo> getWaitBuyTrackList(Long userId, Long trackId, Integer trackCount) {
        //1.根据声音ID查询到“起始”待购声音记录，得到当前声音序号、所属专辑ID
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        Long albumId = trackInfo.getAlbumId();
        Integer orderNum = trackInfo.getOrderNum();

        //2.远程调用用户服务获取已购买声音ID列表
        List<Long> userPaidTrackIdList = userFeignClient.getUserPaidTrackIdList(albumId).getData();

        //3.获取待购声音列表 查询条件：专辑ID，序号，已购声音ID 。排序字段：序号  。返回数量：购买数量
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        //3.1 等值查询专辑标题
        queryWrapper.eq(TrackInfo::getAlbumId, albumId);
        //3.2 大于等于当前选购声音序号
        queryWrapper.ge(TrackInfo::getOrderNum, orderNum);
        if (CollectionUtil.isNotEmpty(userPaidTrackIdList)) {
            //3.3 如果存在已购买声音，去除已购买声音ID
            queryWrapper.notIn(TrackInfo::getId, userPaidTrackIdList);
        }
        //3.4 按照声音序号升序
        queryWrapper.orderByAsc(TrackInfo::getOrderNum);
        //3.5 按用户选择购买数量获取声音列表
        queryWrapper.last("LIMIT " + trackCount);
        //3.6 执行查询列：多查询了专辑ID，需要在订单服务进行算价格需要获取专辑中价格
        queryWrapper.select(TrackInfo::getId, TrackInfo::getTrackTitle, TrackInfo::getCoverUrl, TrackInfo::getAlbumId);
        List<TrackInfo> list = trackInfoMapper.selectList(queryWrapper);
        return list;
    }

}
